/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
 */

#ifndef MCU_LOG_API_H
#define MCU_LOG_API_H
#include <stdint.h>

#define HLINK_MID_STRING_LEN            12 /* 模块ID支持最大的长度 */
#define HLINK_LOG_FLASH_MOVE_BIT        8  /* 写FLASH标志移动位数 */
#define HLINK_LOG_UPLOAD_MOVE_BIT       9

#define HLINK_MID_MASK                  0xFFFF0000  /* 获取模块ID的掩码，取0-15位 */
#define HLINK_FLASH_MASK                0x00000100  /* 获取是否写flash标记掩码，取16-23位 */
#define HLINK_UPLOAD_MASK               0x00000200   /* 日志是否上传网关/手机掩码 */
#define HLINK_LOGLEVEL_MASK             0x000000FF  /* 获取日志级别掩码，取24-31位 */
#define HLINK_LOG_FLASH                 0x00000001  /* 日志写入flash标志 */
#define HLINK_LOG_NO_FLASH              0x00000000  /* 日志不需要写入flash标志 */
#define HLINK_LOG_UPLOAD                0x00000001  /* 日志上传网关/手机标志位 */
#define MCU_LOG_NO_UPLOAD               0x00000000  /* 日志不上传网关/手机标志位 */

/*
 * 定义模块的infoid，在调用日志接口MCU_LOG时使用，每一个infoid会带有四个信息: 模块信息, 是否上传网关/APP、写入flash标志、日志级别;
 * 日志模块会根据这infoid携带的信息做对应的处理。infoid占用4个字节，mid占用前两个字节，flash写入标志占用第三个字节的第一位，upload上传标志占用第三个字节的第2位
 * 日志级别占用第四个字节。
 * 注意: 1)mid定义为4个字节，但是前两个字节有效，规定mid从0x00010000开始，按照0x00020000这样的顺序新增。
 *       2)新增模块需要定义新的mid，如果mid已经被其他模块使用了，是无法注册成功
 */
#define HLINK_INFOID_DEF(mid, upload, flash, log_level)   ((mid) | ((upload) << HLINK_LOG_UPLOAD_MOVE_BIT) | ((flash) << HLINK_LOG_FLASH_MOVE_BIT) | (log_level))

/*
 * HLINK_LOG为记录日志接口，使用示例如下：
 * MCU_LOG(HLINK_COAP_LOG_INFO, "******");
 * HLINK_COAP_LOG_INFO是coap模块定义的infoid，"******"为日志具体的内容
 */

#define MCU_LOG(def, fmt, ...)         hlink_log_api(__FUNCTION__, __LINE__, def, fmt, ##__VA_ARGS__)

/* 日志级别定义 */
typedef enum {
    HLINK_LOG_NONE = 0, /* 初始化级别，无实际意义*/
    HLINK_LOG_EMERG,    /* 非常紧急, 系统不可用 */
    HLINK_LOG_ALERT,    /* 紧急级别, 必须立即采取措施 */
    HLINK_LOG_CRIT,     /* 致命级别 */
    HLINK_LOG_ERROR,    /* 错误级别 */
    HLINK_LOG_WARN,     /* 告警级别 */
    HLINK_LOG_NOTIC,    /* 通知级别 */
    HLINK_LOG_INFO,     /* 信息级别 */
    HLINK_LOG_DEBUG,    /* 调试级别 */
    HLINK_LOG_MAX,
} hlink_log_level;

/*
 * 日志模块属性结构体，开发者可以填充该结构体并调用hlink_log_module_register接口就可以将模块信息注册到日志模块中
 * 注意：1) 参数log_level，支持日志记录/打印的级别，如果级别小于等于这个级别，则会记录/打印该日志
 *       如果log_level = HLINK_LOG_DEBUG，可支持的级别范围(HLINK_LOG_DEBUG-HLINK_LOG_EMERG)
 *       如果log_level = HLINK_LOG_ERROR，可支持的级别范围(HLINK_LOG_ERROR-HLINK_LOG_EMERG)
 */
typedef struct hlink_log_module_info {
    uint32_t mid;                             /* 注册模块id，要保证唯一性，按照0x00010000、0x00020000、0x00030000...定义 */
    char mid_name[HLINK_MID_STRING_LEN + 1];  /* 注册模块名称，名称会在模块打印日志时显示 */
    hlink_log_level log_level;                /* 模块支持显示的日志级别，如果大于这个级别，日志不会被打印 */
} hlink_log_module_info_para;

/* 定义日志写flash函数指针，通过调用hlink_log_write_flash_register接口完成注册，日志模块已经提供具体实现*/
typedef int (*hlink_log_write_flash)(const char *buf, unsigned short len);

/*
 * 记录用户输入日志信息接口
 * 参数func: 表示日志调用时所在的函数名称
 * 参数line_number: 表示日志调用时所在文件的行号
 * 参数infoId: 记录对应的日志id，在日志模块会解析出该infoid对应的内容进行处理
 * 参数format: 日志内容
 * 注意: 1) 日志缓冲区最大支持512字节，其中日志头最大占用46字节，日志内容不应超过466字节
 */
void hlink_log_api(const char *func, int32_t line_number, uint32_t infoId, const char *format, ...);

/*
 * 用户模块信息向日志注册接口
 * 参数hlink_log_module_info_para: 表示日志模块属性结构体
 */
void hlink_log_module_register(hlink_log_module_info_para *log_module_info);

/*
 * 日志信息写入flash功能的函数钩子注册接口
 * 参数hlink_log_write_flash: 日志写flash钩子函数
 */
void hlink_log_write_flash_register(hlink_log_write_flash write);

/*
 * 获取hlink打印日志级别
 * 注意: 1) 返回值为HLINK_LOG_NONE,表示初始级别,实际生效的日志级别为hlink_log_module_register注册时的模块日志级别
 */
hlink_log_level hlink_log_level_get(void);

/*
 * 设置hlink打印日志级别
 * 注意: 1) 调用此接口会导致各模块注册时的模块日志级别失效,各模块均以用户设置的日志级别为准
 *       2) 日志打印级别越高，对性能影响越大
 */
void hlink_log_level_set(hlink_log_level level);

#endif /* MCU_LOG_API_H */
